package main.java.ERMDE.application;

import static org.picocontainer.Characteristics.CACHE;

import java.awt.Color;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import main.java.ERMDE.controllers.AttributeController;
import main.java.ERMDE.controllers.DiagramController;
import main.java.ERMDE.controllers.EntityController;
import main.java.ERMDE.controllers.HierarchyController;
import main.java.ERMDE.controllers.IAttributeController;
import main.java.ERMDE.controllers.IDiagramController;
import main.java.ERMDE.controllers.IEntityController;
import main.java.ERMDE.controllers.IHierarchyController;
import main.java.ERMDE.controllers.IKeysController;
import main.java.ERMDE.controllers.IProjectController;
import main.java.ERMDE.controllers.IRelationshipController;
import main.java.ERMDE.controllers.IRelationshipEntityController;
import main.java.ERMDE.controllers.KeysController;
import main.java.ERMDE.controllers.ProjectController;
import main.java.ERMDE.controllers.RelationshipController;
import main.java.ERMDE.controllers.RelationshipEntityController;
import main.java.ERMDE.controllers.factories.AttributeControllerFactory;
import main.java.ERMDE.controllers.factories.DiagramControllerFactory;
import main.java.ERMDE.controllers.factories.EntityControllerFactory;
import main.java.ERMDE.controllers.factories.HierarchyControllerFactory;
import main.java.ERMDE.controllers.factories.IAttributeControllerFactory;
import main.java.ERMDE.controllers.factories.IDiagramControllerFactory;
import main.java.ERMDE.controllers.factories.IEntityControllerFactory;
import main.java.ERMDE.controllers.factories.IHierarchyControllerFactory;
import main.java.ERMDE.controllers.factories.IKeysControllerFactory;
import main.java.ERMDE.controllers.factories.IRelationshipControllerFactory;
import main.java.ERMDE.controllers.factories.IRelationshipEntityControllerFactory;
import main.java.ERMDE.controllers.factories.KeyControllerFactory;
import main.java.ERMDE.controllers.factories.RelationshipControllerFactory;
import main.java.ERMDE.controllers.factories.RelationshipEntityControllerFactory;
import main.java.ERMDE.infrastructure.FileSystemService;
import main.java.ERMDE.infrastructure.IFileSystemService;
import main.java.ERMDE.infrastructure.IProjectContext;
import main.java.ERMDE.infrastructure.ProjectContext;
import main.java.ERMDE.persistence.DiagramXmlManager;
import main.java.ERMDE.persistence.GraphPersistenceService;
import main.java.ERMDE.persistence.IGraphPersistenceService;
import main.java.ERMDE.validation.IProjectValidationService;
import main.java.ERMDE.validation.ProjectValidationService;
import main.java.ERMDE.validation.metrics.AttributesPerEntityValidator;
import main.java.ERMDE.validation.metrics.AttributesPerRelationshipValidator;
import main.java.ERMDE.validation.metrics.EntitiesPerDiagramValidator;
import main.java.ERMDE.validation.metrics.EntitiesPerHierarchyValidator;
import main.java.ERMDE.validation.metrics.EntitiesPerRelationshipValidator;
import main.java.ERMDE.validation.metrics.HierarchiesPerDiagramValidator;
import main.java.ERMDE.validation.metrics.IMetricsCalculator;
import main.java.ERMDE.validation.metrics.MetricsCalculator;
import main.java.ERMDE.validation.metrics.RelationshipsPerDiagramValidator;
import main.java.ERMDE.validation.rules.AttributeCardinalityValidator;
import main.java.ERMDE.validation.rules.DiagramMustHaveEntitiesValidator;
import main.java.ERMDE.validation.rules.DiagramShouldHaveRelationshipsValidator;
import main.java.ERMDE.validation.rules.EntityTypeValidator;
import main.java.ERMDE.validation.rules.RelationshipCardinalityValidator;
import main.java.ERMDE.views.AttributeView;
import main.java.ERMDE.views.DiagramView;
import main.java.ERMDE.views.EntityView;
import main.java.ERMDE.views.HierarchyView;
import main.java.ERMDE.views.IAttributeView;
import main.java.ERMDE.views.IDiagramView;
import main.java.ERMDE.views.IEntityView;
import main.java.ERMDE.views.IHierarchyView;
import main.java.ERMDE.views.IKeysView;
import main.java.ERMDE.views.IProjectView;
import main.java.ERMDE.views.IRelationshipEntityView;
import main.java.ERMDE.views.IRelationshipView;
import main.java.ERMDE.views.KeyView;
import main.java.ERMDE.views.RelationshipEntityViewImpl;
import main.java.ERMDE.views.RelationshipViewImpl;
import main.java.FSMDE.controllers.ComponentController;
import main.java.FSMDE.controllers.IComponentController;
import main.java.FSMDE.controllers.IInterfaceController;
import main.java.FSMDE.controllers.ITransitionController;
import main.java.FSMDE.controllers.factories.ComponentControllerFactory;
import main.java.FSMDE.controllers.factories.IComponentControllerFactory;
import main.java.FSMDE.validation.rules.ButtonComponentMustReferenceTransitionValidator;
import main.java.FSMDE.validation.rules.ComplexComponentShouldHaveComponentsValidator;
import main.java.FSMDE.validation.rules.DiagramEndInterfaceValidator;
import main.java.FSMDE.validation.rules.DiagramMustHaveInterfacesValidator;
import main.java.FSMDE.validation.rules.DiagramStartInterfaceValidator;
import main.java.FSMDE.validation.rules.DiagramUniqueStartInterfaceValidator;
import main.java.FSMDE.validation.rules.GenericComponentTypeValidator;
import main.java.FSMDE.validation.rules.InterfaceMustHaveComponentsValidator;
import main.java.FSMDE.validation.rules.InterfaceShouldBeDestinationValidator;
import main.java.FSMDE.validation.rules.InterfaceShouldTriggerTransitionValidator;
import main.java.FSMDE.validation.rules.TransitionControlShouldBeTriggerredValidator;
import main.java.FSMDE.validation.rules.TransitionWithoutDestinationValidator;
import main.java.FSMDE.validation.rules.TransitionWithoutSourceValidator;
import main.java.FSMDE.validation.rules.URLComponentMustReferenceTransitionValidator;
import main.java.FSMDE.views.ComponentView;
import main.java.FSMDE.views.IComponentView;
import main.java.FSMDE.views.IInterfaceView;
import main.java.FSMDE.views.ITransitionView;
import main.java.FSMDE.views.InterfaceView;
import main.java.FSMDE.views.TransitionView;
import main.java.common.IXmlFileManager;
import main.java.common.IXmlManager;
import main.java.common.ProjectView;
import main.java.common.XmlFileManager;


import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.MutablePicoContainer;


import com.mxgraph.canvas.mxGraphics2DCanvas;
import com.mxgraph.io.mxCodecRegistry;
import com.mxgraph.io.mxObjectCodec;
import com.mxgraph.shape.mxIMarker;
import com.mxgraph.shape.mxMarkerRegistry;
import com.mxgraph.util.mxPoint;
import com.mxgraph.view.mxCellState;

public class Bootstrapper {

	private MutablePicoContainer container;

	public MutablePicoContainer getContainer() {
		return this.container;
	}

	public void run() {
		this.container = this.createContainer();
		this.configureContainer();
		this.registerJGraphExtensions();
	}

	private void registerJGraphExtensions() {
		mxMarkerRegistry.registerMarker("emptyCircle", new EmptyCircleMarker());
		mxMarkerRegistry.registerMarker("emptyRedCircle",
				new EmptyRedCircleMarker());
		mxMarkerRegistry.registerMarker("filledRedCircle",
				new RedCircleMarker());
		mxMarkerRegistry.registerMarker("classStructure",
			new ClassStructureMarker());
		mxMarkerRegistry.registerMarker("emptyTriangle",
			new EmptyTriangleMarker());
		mxMarkerRegistry.registerMarker("emptyDiamond",
			new EmptyDiamondMarker());
		mxMarkerRegistry.registerMarker("simpleArrow",
			new SimpleArrowMarker());
		mxCodecRegistry.addPackage("java.util.UUID");
		mxCodecRegistry.register(new mxObjectCodec(new java.util.UUID(0, 0)));

	}

	private void configureContainer() {
		this.container
				.as(CACHE)
				.addComponent(MutablePicoContainer.class, this.container)
				.addComponent(IDiagramController.class, DiagramController.class)
				.addComponent(main.java.FSMDE.controllers.IDiagramController.class,
						main.java.FSMDE.controllers.DiagramController.class)
				.addComponent(main.java.DMDE.controllers.IDiagramController.class,
						main.java.DMDE.controllers.DiagramController.class)		
				.addComponent(IDiagramView.class, DiagramView.class)
				.addComponent(main.java.FSMDE.views.IDiagramView.class,
						main.java.FSMDE.views.DiagramView.class)
				.addComponent(main.java.DMDE.view.IDiagramView.class,
						main.java.DMDE.view.DiagramView.class)	
				.addComponent(IXmlManager.class, DiagramXmlManager.class)
				.addComponent(main.java.FSMDE.persistence.IXmlManager.class,
						main.java.FSMDE.persistence.DiagramXmlManager.class)
				.as(CACHE)
				.addComponent(IXmlFileManager.class, XmlFileManager.class)
				.as(CACHE)
				.addComponent(main.java.FSMDE.infrastructure.IProjectContext.class,
						main.java.FSMDE.infrastructure.ProjectContext.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.infrastructure.IProjectContext.class,
						main.java.DMDE.infrastructure.ProjectContext.class)		
				.as(CACHE)
				.addComponent(IProjectContext.class, ProjectContext.class)
				.as(CACHE)
				.addComponent(IEntityControllerFactory.class,
						EntityControllerFactory.class)
				.addComponent(IEntityController.class, EntityController.class)
				.addComponent(IEntityView.class, EntityView.class)
				.addComponent(IInterfaceView.class, InterfaceView.class)
				.addComponent(ITransitionView.class, TransitionView.class)
				.as(CACHE)
				.addComponent(IRelationshipControllerFactory.class,
						RelationshipControllerFactory.class)
				.addComponent(IRelationshipController.class,
						RelationshipController.class)
				.as(CACHE)
				.addComponent(
						main.java.FSMDE.controllers.factories.ITransitionControllerFactory.class,
						main.java.FSMDE.controllers.factories.TransitionControllerFactory.class)
				.addComponent(ITransitionController.class,
						main.java.FSMDE.controllers.TransitionController.class)
				.as(CACHE)
				.addComponent(
						main.java.FSMDE.controllers.factories.IInterfaceControllerFactory.class,
						main.java.FSMDE.controllers.factories.InterfaceControllerFactory.class)
				.addComponent(IInterfaceController.class,
						main.java.FSMDE.controllers.InterfaceController.class)
				.addComponent(IRelationshipView.class,
						RelationshipViewImpl.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IClassControllerFactory.class,
						main.java.DMDE.controllers.factories.ClassControllerFactory.class)
				.addComponent(main.java.DMDE.controllers.IClassController.class, 
					main.java.DMDE.controllers.ClassController.class)
				.addComponent(main.java.DMDE.view.IClassView.class, 
					main.java.DMDE.view.ClassView.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IAttributeControllerFactory.class,
						main.java.DMDE.controllers.factories.AttributeControllerFactory.class)	
				.addComponent(main.java.DMDE.controllers.IAttributeController.class, 
					main.java.DMDE.controllers.AttributeController.class)
				.addComponent(main.java.DMDE.view.IAttributeView.class, 
					main.java.DMDE.view.AttributeView.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IOperationControllerFactory.class,
						main.java.DMDE.controllers.factories.OperationControllerFactory.class)	
				.addComponent(main.java.DMDE.controllers.IOperationController.class, 
					main.java.DMDE.controllers.OperationController.class)
				.addComponent(main.java.DMDE.view.IOperationView.class, 
					main.java.DMDE.view.OperationView.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IInterfaceControllerFactory.class,
						main.java.DMDE.controllers.factories.InterfaceControllerFactory.class)	
				.addComponent(main.java.DMDE.controllers.IInterfaceController.class, 
					main.java.DMDE.controllers.InterfaceController.class)
				.addComponent(main.java.DMDE.view.IInterfaceView.class, 
					main.java.DMDE.view.InterfaceView.class)	
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IParameterControllerFactory.class,
						main.java.DMDE.controllers.factories.ParameterControllerFactory.class)	
				.addComponent(main.java.DMDE.controllers.IParameterController.class, 
					main.java.DMDE.controllers.ParameterController.class)
				.addComponent(main.java.DMDE.view.IParameterView.class, 
					main.java.DMDE.view.ParameterView.class)	
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IRelationshipControllerFactory.class,
						main.java.DMDE.controllers.factories.RelationshipControllerFactory.class)	
				.addComponent(main.java.DMDE.controllers.IRelationshipController.class, 
					main.java.DMDE.controllers.RelationshipController.class)
				.addComponent(main.java.DMDE.view.RelationshipView.class, 
					main.java.DMDE.view.RelationshipView.class)
				.as(CACHE)
				.addComponent(IRelationshipEntityControllerFactory.class,
						RelationshipEntityControllerFactory.class)
				.addComponent(IRelationshipEntityController.class,
						RelationshipEntityController.class)
				.addComponent(IRelationshipEntityView.class,
						RelationshipEntityViewImpl.class)
				.as(CACHE)
				.addComponent(IHierarchyControllerFactory.class,
						HierarchyControllerFactory.class)
				.addComponent(IHierarchyController.class,
						HierarchyController.class)
				.addComponent(IHierarchyView.class, HierarchyView.class)
				.as(CACHE)
				.addComponent(IKeysControllerFactory.class,
						KeyControllerFactory.class)
				.addComponent(IKeysController.class, KeysController.class)
				.addComponent(IKeysView.class, KeyView.class)
				.as(CACHE)
				.addComponent(IGraphPersistenceService.class,
						GraphPersistenceService.class)
				.as(CACHE)
				.addComponent(main.java.FSMDE.persistence.IGraphPersistenceService.class,
						main.java.FSMDE.persistence.GraphPersistenceService.class)
				.as(CACHE)
				.addComponent(IDiagramControllerFactory.class,
						DiagramControllerFactory.class)
				.as(CACHE)
				.addComponent(main.java.DMDE.controllers.factories.IDiagramControllerFactory.class,
					main.java.DMDE.controllers.factories.DiagramControllerFactory.class)		
				.as(CACHE)
				.addComponent(
						main.java.FSMDE.controllers.factories.IDiagramControllerFactory.class,
						main.java.FSMDE.controllers.factories.DiagramControllerFactory.class)
				.addComponent(IProjectController.class, ProjectController.class)
				.addComponent(IProjectView.class, ProjectView.class)
				.addComponent(IAttributeController.class,
						AttributeController.class)
				.as(CACHE)
				.addComponent(IProjectValidationService.class,
						ProjectValidationService.class)
				.as(CACHE)
				.addComponent(main.java.FSMDE.validation.IProjectValidationService.class,
						main.java.FSMDE.validation.ProjectValidationService.class)
				.as(CACHE)
				.addComponent(
						ButtonComponentMustReferenceTransitionValidator.class)
				.as(CACHE)
				.addComponent(DiagramEndInterfaceValidator.class)
				.as(CACHE)
				.addComponent(DiagramMustHaveInterfacesValidator.class)
				.as(CACHE)
				.addComponent(DiagramStartInterfaceValidator.class)
				.as(CACHE)
				.addComponent(GenericComponentTypeValidator.class)
				.as(CACHE)
				.addComponent(InterfaceMustHaveComponentsValidator.class)
				.as(CACHE)
				.addComponent(TransitionWithoutDestinationValidator.class)
				.as(CACHE)
				.addComponent(TransitionWithoutSourceValidator.class)
				.as(CACHE)
				.addComponent(
						URLComponentMustReferenceTransitionValidator.class)
				.as(CACHE)
				.addComponent(DiagramUniqueStartInterfaceValidator.class)
				.as(CACHE)
				.addComponent(InterfaceShouldBeDestinationValidator.class)
				.as(CACHE)
				.addComponent(InterfaceShouldTriggerTransitionValidator.class)
				.as(CACHE)
				.addComponent(
						ComplexComponentShouldHaveComponentsValidator.class)
				.as(CACHE)
				.addComponent(
						TransitionControlShouldBeTriggerredValidator.class)

				.as(CACHE)
				.addComponent(IMetricsCalculator.class, MetricsCalculator.class)
				.as(CACHE)
				.addComponent(IFileSystemService.class, FileSystemService.class)
				.as(CACHE)
				.addComponent(IAttributeControllerFactory.class,
						AttributeControllerFactory.class)
				.as(CACHE)
				.addComponent(RelationshipsPerDiagramValidator.class)
				.as(CACHE)
				.addComponent(EntitiesPerDiagramValidator.class)
				.as(CACHE)
				.addComponent(EntitiesPerHierarchyValidator.class)
				.as(CACHE)
				.addComponent(EntitiesPerRelationshipValidator.class)
				.as(CACHE)
				.addComponent(HierarchiesPerDiagramValidator.class)
				.as(CACHE)
				.addComponent(AttributesPerRelationshipValidator.class)
				.as(CACHE)
				.addComponent(AttributesPerEntityValidator.class)
				.as(CACHE)
				.addComponent(RelationshipCardinalityValidator.class)
				.as(CACHE)
				.addComponent(EntityTypeValidator.class)
				.as(CACHE)
				.addComponent(AttributeCardinalityValidator.class)
				.as(CACHE)
				.addComponent(DiagramShouldHaveRelationshipsValidator.class)
				.as(CACHE)
				.addComponent(DiagramMustHaveEntitiesValidator.class)
				.addComponent(IAttributeView.class, AttributeView.class)
				.as(CACHE)
				.addComponent(IComponentControllerFactory.class,
						ComponentControllerFactory.class)
				.addComponent(IComponentController.class,
						ComponentController.class)
				.addComponent(IComponentView.class, ComponentView.class);
	}

	public MutablePicoContainer createContainer() {
		return new DefaultPicoContainer();
	}

}

class ClassStructureMarker implements mxIMarker{

    private Shape mShapeOne;
    
    @Override
    public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state,
		String type, mxPoint pe, double nx, double ny, double size) {

	mShapeOne = new Rectangle2D.Double(nx, ny , size, size);
	Shape mShapeTwo = new Rectangle2D.Double(nx, ny + size, size, size);
	
	mShapeOne.intersects((Rectangle2D) mShapeTwo);
	canvas.getGraphics().setColor(Color.black);
	canvas.getGraphics().draw(mShapeOne);
	canvas.getGraphics().fill(mShapeOne);
	
	return new mxPoint(-nx , -ny);
	
    }
    
}

abstract class CircleMarker implements mxIMarker {
	protected double cx, cy, a;
	protected Shape shape;

	protected void inicializarAtributos(mxPoint pe, double nx, double ny,
			double size) {
		cx = pe.getX() - nx / 2;
		cy = pe.getY() - ny / 2;
		a = size / 2;
		shape = new Ellipse2D.Double(cx - a, cy - a, size, size);
	}

}

class EmptyCircleMarker extends CircleMarker {

	public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state,
			String type, mxPoint pe, double nx, double ny, double size) {
		inicializarAtributos(pe, nx, ny, size);
		canvas.getGraphics().draw(shape);
		return new mxPoint(-nx / 2, -ny / 2);
	}

}

class EmptyRedCircleMarker extends CircleMarker {
	public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state,
			String type, mxPoint pe, double nx, double ny, double size) {
		inicializarAtributos(pe, nx, ny, size);
		Color originalColor = canvas.getGraphics().getColor();
		canvas.getGraphics().setColor(Color.RED);
		canvas.getGraphics().draw(shape);
		canvas.getGraphics().setColor(originalColor);
		return new mxPoint(-nx / 2, -ny / 2);
	}
}

class RedCircleMarker extends CircleMarker {
	public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state,
			String type, mxPoint pe, double nx, double ny, double size) {
		inicializarAtributos(pe, nx, ny, size);
		Color originalColor = canvas.getGraphics().getColor();
		canvas.getGraphics().setColor(Color.RED);
		canvas.getGraphics().draw(shape);
		canvas.getGraphics().fill(shape);
		canvas.getGraphics().setColor(originalColor);

		return new mxPoint(-nx / 2, -ny / 2);
	}
}

class EmptyTriangleMarker implements mxIMarker {

    @Override
    public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state, String type, mxPoint pe, double nx, double ny, double size) {
    	Polygon poly = new Polygon();
    	poly.addPoint((int) Math.round(pe.getX()), (int) Math.round(pe.getY()));
	poly.addPoint((int) Math.round(pe.getX() - nx - ny / 2), (int) Math.round(pe.getY() - ny + nx / 2));
	poly.addPoint((int) Math.round(pe.getX() + ny / 2 - nx), (int) Math.round(pe.getY() - ny - nx / 2));
	canvas.getGraphics().draw(poly);
	return new mxPoint((int) Math.round(-nx), (int) Math.round(-ny));
    }
}    
    
class EmptyDiamondMarker implements mxIMarker {

    @Override
    public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state, String type, mxPoint pe, double nx, double ny, double size) {	        	
    	Polygon poly = new Polygon();
	poly.addPoint((int) Math.round(pe.getX()),(int) Math.round(pe.getY()));
	poly.addPoint((int) Math.round(pe.getX() - nx / 2 - ny / 2), (int) Math.round(pe.getY() + nx / 2 - ny / 2));
	poly.addPoint((int) Math.round(pe.getX() - nx),	(int) Math.round(pe.getY() - ny));
	poly.addPoint((int) Math.round(pe.getX() - nx / 2 + ny / 2),(int) Math.round(pe.getY() - ny / 2 - nx / 2));
	canvas.getGraphics().draw(poly);
	return new mxPoint(-nx, -ny);
    }
}	

class SimpleArrowMarker implements mxIMarker {

    @Override
    public mxPoint paintMarker(mxGraphics2DCanvas canvas, mxCellState state, String type, mxPoint pe, double nx, double ny, double size) {
    	canvas.getGraphics().drawLine((int) pe.getX(), (int) pe.getY(), (int) Math.round(pe.getX() - nx - ny / 3), (int) Math.round(pe.getY() + nx / 3 - ny));
    	canvas.getGraphics().drawLine((int) pe.getX(), (int) pe.getY(), (int) Math.round(pe.getX() - nx + ny / 3), (int) Math.round(pe.getY() - nx / 3 - ny));
	return new mxPoint(0, 0);
    }
}
	
